/* Copyright (c) 2012-2014 Adobe Systems Incorporated. All rights reserved.

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


    BACKGROUND_WIDTH  = 480;
    BACKGROUND_HEIGHT = 480;
    IMAGE_WIDTH  = 400;
    IMAGE_HEIGTH = 400;
    GRID_WIDTH   = 100;
    GRID_HEIGHT  = 100;
    GRID_SPACE   = 16;
    ROW = 4;
    COL = 4;
    SCALEX = SCALEY = 1;

    loader = null;
    bitmapList = [];
    backLayer = null, frontLayer = null;
    towk = null;
    pos2sprite = {}

    log2_map_value = {0:0,2:1,4:2,8:3,16:4,32:5,64:6,128:7,256:8,512:9,1024:10};

    function log2(value){return log2_map_value[value];}

    function main() {
        LGlobal.canTouch = true;
        loader = new LLoader();
        loader.addEventListener(LEvent.COMPLETE,loadBackgroundData);
        loader.load("img/number.png","bitmapData");
    }

    function onNewNumber(event){
        console.log("new number:" + event.x + "," + event.y + "," + bitmapList[0].scaleY);
        towk.print_empty();
        var button = bitmapList[log2(event.value)].clone();
        button.visible = false;
        leftx = (event.y  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE)  * bitmapList[0].scaleY;
        lefty = (event.x  *(GRID_HEIGHT + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleX;
        button.x = leftx;button.y = lefty;
        button.scaleX = button.scaleY = bitmapList[0].scaleX;
        if(pos2sprite[towk.vector2index(event.x,event.y)]) console.error("position : " + event.x + "," + event.y + " already has sprite.");
        pos2sprite[towk.vector2index(event.x,event.y)] = button;
        LTweenLite.to(button,0.01,{scaleX : 0.1, scaleY : 0.1, x: leftx + GRID_WIDTH * SCALEX / 2 , y:lefty + GRID_HEIGHT * SCALEY / 2});
        button.visible = true;
        frontLayer.addChild(button);
        LTweenLite.to(button,0.01,{scaleX : 0.1, scaleY : 0.1, x: leftx + GRID_WIDTH * SCALEX / 2 , y:lefty + GRID_HEIGHT * SCALEY / 2 }).to(
        button,0.09,{scaleX : SCALEX, scaleY : SCALEY, x : leftx, y : lefty }); 
        console.log("add spirte :" + towk.vector2index(event.x,event.y));
    }

    function onMove(event){
        towk.print_empty();
        console.log("move from [" + event.src.x + "," + event.src.y + "]  to [" + event.des.x + "," + event.des.y + "]");
        if(pos2sprite[towk.vector2index(event.des.x,event.des.y)]) console.error("position : " + event.des.x + "," + event.des.y + " already has sprite.");
        var button = pos2sprite[towk.vector2index(event.src.x,event.src.y)];
        delete  pos2sprite[towk.vector2index(event.src.x,event.src.y)];
        console.log("remove spirte :" + towk.vector2index(event.src.x,event.src.y));
        pos2sprite[towk.vector2index(event.des.x,event.des.y)] = button;
        console.log("add spirte :" + towk.vector2index(event.des.x,event.des.y));
        LTweenLite.to(
            button,
            0.05,
            {
                x : (event.des.y  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleY,
                y : (event.des.x  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleX
            }
        );
    }

    function onMoveMerge(event){
        towk.print_empty();
        console.log("merge [" + event.src.x + "," + event.src.y + "]  to [" + event.des.x + "," + event.des.y + "]");
        var srcButton = pos2sprite[towk.vector2index(event.src.x,event.src.y)];
        var desButton = pos2sprite[towk.vector2index(event.des.x,event.des.y)];
        delete pos2sprite[towk.vector2index(event.src.x,event.src.y)];
        delete pos2sprite[towk.vector2index(event.des.x,event.des.y)];
        console.log("remove spirte :" + towk.vector2index(event.src.x,event.src.y));
        console.log("remove spirte :" + towk.vector2index(event.des.x,event.des.y));
        LTweenLite.to(
            srcButton,
            0.05,
            {
                x : (event.des.y  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleY,
                y : (event.des.x  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleX,
                onComplete : function(btn){
                    frontLayer.removeChild(srcButton);
                    frontLayer.removeChild(desButton);
                    srcButton = bitmapList[log2(event.value)].clone();
                    pos2sprite[towk.vector2index(event.des.x,event.des.y)] = srcButton;
                    console.log("add spirte :" + towk.vector2index(event.des.x,event.des.y));
                    srcButton.x = (event.des.y  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleX;
                    srcButton.y = (event.des.x  *(GRID_WIDTH + GRID_SPACE) + GRID_SPACE) * bitmapList[0].scaleX;
                    frontLayer.addChild(srcButton);
                }
            }
        );
    }

    function loadBackgroundData(event){
        var list = new LGlobal.divideCoordinate(IMAGE_WIDTH,IMAGE_WIDTH,ROW,COL);
        backLayer = new LSprite();
        addChild(backLayer);
        frontLayer = new LSprite();
        addChild(frontLayer);
        var fps = new FPS();
        addChild(fps);
        SCALEX = SCALEY = window.innerWidth / BACKGROUND_WIDTH;
        for(var i = 0; i < ROW; i++)
        for(var j = 0; j < COL ;j++) {
            var bitmapData = new LBitmapData(loader.content, 0, 0, GRID_WIDTH, GRID_HEIGHT);
            bitmapData.x = list[i][j].x;
            bitmapData.y = list[i][j].y;
            var bitmap = new LBitmap(bitmapData);
            bitmap.scaleX = bitmap.scaleY = SCALEX;
            if(i == 0 && j == 0) bitmapList.push(bitmap);
            bitmapList.push(bitmap);
        }
        towk.start();
    }

    /*
    GameEvent
     */
    var GameEvent = function(){throw  "gameevent cann't be intingsed!"};
    GameEvent.MOVE_EVENT = "moveevent";
    GameEvent.MOVE_MERGE_EVENT = "movemergeevent";
    GameEvent.NEW_EVENT = "newevent";
    GameEvent.NO_SPACE_EVENT = "nospaceevent";
    GameEvent.TIMEOUT_EVENT = "timeoutevent";

    /*
    GameLogic
     */
    function TwoK(row, col){
      this._row = row;
      this._col = col;
      this._map = [];
      this._empty_pos = [];
      this._listeners = {};
      for(var i = 0; i < row; ++i)
      {
        for(var j = 0; j < col; ++j)
        {
          this._map.push(0);
          this._empty_pos.push(this.vector2index(i,j));
        }
      }
    }
    TwoK.prototype = {
        addEventListener : function(type,fn){
          if(this._listeners[type])this._listeners[type].push(fn);
          else this._listeners[type] = [fn];
        },
        removeEventListener : function(type,fn){
            if(this._listeners[type]) this._listeners[type].splice($.inArray(fn,this._listeners[type]),1);
        },
        clearEventListener : function (type){
            if(this._listeners[type]) delete this._listeners[type];
        },
        notice : function(event){
            if(this._listeners[event.type]){
                for(var idx in this._listeners[event.type]) {
                    this._listeners[event.type][idx](event);
                }
            }
        },
        index2vector : function (index) {
            return { x:parseInt(index / this._col), y:index % this._col};
        },
        vector2index : function (x,y) {
            return x * this._col + y;
        },
        newNumber : function(){
            if(this._empty_pos.length <= 0){
                this.notice({
                    type : GameEvent.NO_SPACE_EVENT
                });
                return;
            }
            var pos = parseInt(Math.random() * this._empty_pos.length);
            var vector = this.index2vector(this._empty_pos[pos]);
            var value = Math.random() > 0.9 ? 4 : 2;
            this._map[this._empty_pos[pos]] = value;
            this._empty_pos.splice(pos,1);
            this.notice({
                 type : GameEvent.NEW_EVENT,
                 x : vector.x,
                 y : vector.y,
                 value : value
            });
        },

        print_empty : function()
        {
          var info = "[";
            for(var index in this._empty_pos){
                info += this._empty_pos[index] + ",";
            }
            info += "]";
            console.log(info);
        },

        get_edge : function(index,direction){
            vector = this.index2vector(index)
            if(direction == "left"){
                for(i = 0; i < vector.y; ++i){
                    var idx = this.vector2index(vector.x,i);
                    if(this._map[idx] <= 0) return idx;
                }
            }
            else if(direction == "right"){
                for(i = 3 ; i > vector.y; --i){
                    var idx = this.vector2index(vector.x,i);
                    if(this._map[idx] <= 0) return idx;
                }
            }
            else if(direction == "up"){
                for(i = 0; i < vector.x; ++i){
                    var idx = this.vector2index(i,vector.y);
                    if(this._map[idx] <= 0) return idx;
                }
            }else{
                for(i = 3; i > vector.x; --i) {
                    var idx = this.vector2index(i, vector.y);
                    if (this._map[idx] <= 0) return idx;
                }
            }
            return index;
        },

        swipe : function(direction){
            var rows = [0,1,2,3];
            var cols = [0,1,2,3];
            if(direction == "down") rows = rows.reverse();
            if(direction == "right") cols = cols.reverse();
            var edges = null;
            var moved = false;
            if(direction == "left" || direction == "right") edges = [this.vector2index(rows[0],cols[0]),this.vector2index(rows[1],cols[0]),this.vector2index(rows[2],cols[0]),this.vector2index(rows[3],cols[0]),"V"]
            else edges = [this.vector2index(rows[0],cols[0]), this.vector2index(rows[0],cols[1]),this.vector2index(rows[0],cols[2]),this.vector2index(rows[0],cols[3]),"H"];
            for(var i in rows)
            {
                for(var j in cols){
                    var row = rows[i], col = cols[j];
                    var index = this.vector2index(row,col);
                    var edge_index = edges[4] == "V" ? i : j;
                    if(edges[edge_index] == index) continue;//最边缘的，不动
                    else if(this._map[edges[edge_index]] == 0){//边缘是0
                        if(this._map[index] > 0){//当前格子有值，则移动格子,边缘不变
                            this._map[edges[edge_index]] = this._map[index];
                            this._map[index] = 0;
                            this._empty_pos.splice($.inArray(edges[edge_index],this._empty_pos),1);
                            this._empty_pos.push(index);
                            this.notice({
                                type : GameEvent.MOVE_EVENT,
                                src : this.index2vector(index),
                                des : this.index2vector(edges[edge_index]),
                                value : this._map[index]
                                });
                            moved = true;
                            //edges[edge_index] = this.get_edge(index,direction);//更新边缘
                        }else{//当前格子无值，边缘不变
                        }
                    }else{//边缘不是0
                        if(this._map[edges[edge_index]] == this._map[index])//合并,边缘不变
                        {
                            this._map[edges[edge_index]] *= 2;
                            this._map[index] = 0;
                            this._empty_pos.push(index);
                            this.notice({
                                type : GameEvent.MOVE_MERGE_EVENT,
                                src : this.index2vector(index),
                                des : this.index2vector(edges[edge_index]),
                                value : this._map[edges[edge_index]]
                            });
                            edges[edge_index] = this.get_edge(index,direction);
                            moved = true;
                        }
                        else if(this._map[index] > 0){//不合并，更新边缘到最近的非空位置
                            edges[edge_index] = this.get_edge(index,direction);
                            if(edges[edge_index] != index){
                                this._map[edges[edge_index]] = this._map[index];
                                this._map[index] = 0;
                                this._empty_pos.splice($.inArray(edges[edge_index],this._empty_pos),1);
                                this._empty_pos.push(index);
                                this.notice({
                                    type : GameEvent.MOVE_EVENT,
                                    src : this.index2vector(index),
                                    des : this.index2vector(edges[edge_index]),
                                    value : this._map[index]
                                    });
                                }
                            moved = true;
                        }
                        else{//不合并，不改变边缘
                        }
                    }

                }
            }
            if(moved){
                this.newNumber();
            }
            return moved;
        },
        start : function(){
            this.newNumber();
        }

    };
